﻿using System;
using System.Collections.Generic;
using Hammock;
using System.Xml.Serialization;
using Hammock.Web;
using MailChimpAmazon.SES.Results;
using System.Text.RegularExpressions;

namespace MailChimpAmazon.SES
{
	public class SesApi
	{
		internal const String ParamApiKey = "apikey";

		private const String ApiUrlTemplate = "http://us{0}.sts.mailchimp.com/1.0/";
		private const String ApiUserAgentName = "NogginBox SES Wrapper";

		internal String Key { get; private set;}

		private String _apiUrl;
		/// <summary>
		/// The URL taking into account the API Key server num bit
		/// </summary>
		private String ApiUrl
		{
			get
			{
				if(_apiUrl == null)
				{
					try
					{
						var serverNumReg = new Regex("-us([0-9])$");
						var serverNumMatch = serverNumReg.Match(Key);
						var serverNum = serverNumMatch.Groups[1].Value;

						_apiUrl = String.Format(ApiUrlTemplate, serverNum);
					}
					catch(Exception e)
					{
						throw new Exception("Unable to work out API url for provided key.", e);
					}
				}
				return _apiUrl;
			}
		}

		public SesApi(String key)
		{
			Key = key;
		}
		
		internal RestClient SesClient
		{
			get
			{
				if (_restClient == null)
				{
					XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
					namespaces.Add(string.Empty, string.Empty);

					var serializer = new Hammock.Serialization.HammockDataContractJsonSerializer();

					_restClient = new RestClient
					{
						Authority = ApiUrl,
						Serializer = serializer,
						Deserializer = serializer
					};

					_restClient.AddHeader("User-Agent", ApiUserAgentName + "1");
				}
				return _restClient;
			}
		}
		private RestClient _restClient;

		#region API call methods

		public GetSendQuotaResult GetSendQuota()
		{
			var client = SesClient;
			var request = CreateRequest("GetSendQuota");
			var response = client.Request<GetSendQuotaResult>(request);
			checkForError(response);

			var result = response.ContentEntity;
			return result;
		}

		public IEnumerable<GetSendStatisticsResult> GetSendStatistics()
		{
			var client = SesClient;
			var request = CreateRequest("GetSendStatistics");
			var response = client.Request<IEnumerable<GetSendStatisticsResult>>(request);
			checkForError(response);

			var result = response.ContentEntity;
			return result;
		}

		/// <summary>
		/// Composes an email message based on input data, and then immediately queues the message for sending. MailChimp manage requeuing for you, so if a message gets rejected for a queueing issue, you've completely blown out your Amazon sending limits.
		/// </summary>
		/// <param name="subject">The message subject</param>
		/// <param name="htmlMessage">The full HTML content to be sent</param>
		/// <param name="textMessage">The full Text content to be sent. (optional, pass in null if you don't want this)</param>
		/// <param name="fromEmail">A verified email address from ListVerifiedEmailAddresses.</param>
		/// <param name="toEmailAddresses">A collection containing up to 50 email addresses to receive this email.</param>
		/// <param name="replyToEmails">The email address(es) that should be set as reply-to email addresses. (optional, pass in null if you don't want this) [Amazon currently seem to struggle if you set more than one address 2/2/2011]</param>
		/// <param name="trackOpens">Whether or not to turn on MailChimp-specific opens tracking. (default: false)</param>
		/// <param name="trackClicks">Whether or not to turn on MailChimp-specific click tracking. (default: false)</param>
		/// <param name="tags">A collection of strings to tag the message with. Stats can be accumulated using tags, though MailChimp only store the first 100 we see, so this should not be unique or change frequently. Tags starting with underscore are reserved and will cause errors.</param>
		/// <returns></returns>
		public SendEmailResult SendEmail(String subject,
								String htmlMessage,
								String textMessage,
								EmailAddress fromEmail,
								IEnumerable<EmailAddress> toEmailAddresses,
								IEnumerable<String> replyToEmails = null,
								bool trackOpens = false,
								bool trackClicks = false,
								IEnumerable<String> tags = null)
		{


			var client = SesClient;
			var request = CreateRequest("SendEmail", WebMethod.Post);

			// Message
			request.AddParameter("message[subject]", subject);
			request.AddParameter("message[html]", htmlMessage);
			if (textMessage != null) request.AddParameter("message[text]", textMessage);
			request.AddParameter("message[from_email]", fromEmail.Email);
			request.AddParameter("message[from_name]", fromEmail.Name);

			// Message recipients
			var emailCount = 0;
			foreach (var email in toEmailAddresses)
			{
				request.AddParameter(String.Format("message[to_name][{0}]", emailCount),
													email.Name);
				request.AddParameter(String.Format("message[to_email][{0}]", emailCount),
													email.Email);
				emailCount++;
			}

			// Message recipients
			if (replyToEmails != null)
			{
				emailCount = 0;
				foreach (var email in replyToEmails)
				{
					request.AddParameter(String.Format("message[reply_to][{0}]", emailCount),
														email);
					emailCount++;
				}
			}

			// Options
			request.AddParameter("track_opens", trackOpens.ToString());
			request.AddParameter("track_clicks", trackClicks.ToString());

			// Tags
			if (tags != null)
			{
				var tagCount = 0;
				foreach (var tag in tags)
				{
					request.AddParameter(String.Format("tags[{0}]", tagCount), tag);
					tagCount++;
				}
			}


			var response = client.Request<SendEmailResult>(request);

			var result = response.ContentEntity;
			if (!(result != null && (result.aws_code == "Throttling" || result.aws_code == "MessageRejected")))
			{
				checkForError(response);
			}
			else
			{
				switch(result.aws_code)
				{
					case "Throttling":
						result.status = "throttled";
						result.msg = "Not sent due to throttling";
						break;
					case "MessageRejected":
						result.status = "rejected";
						result.msg = "The message was rejected";
						break;
				}
			}

			return result;
		}

		public ListVerifiedEmailAddressesResult ListVerifiedEmailAddresses()
		{
			var client = SesClient;
			var request = CreateRequest("ListVerifiedEmailAddresses");
			var response = client.Request<ListVerifiedEmailAddressesResult>(request);
			checkForError(response);

			var result = response.ContentEntity;
			return result;

		}

		public void VerifyEmailAddress(String emailAddess)
		{
			var client = SesClient;
			var request = CreateRequest("VerifyEmailAddress", WebMethod.Post);
			request.AddParameter("email", emailAddess);

			var response = client.Request(request);
			checkForError(response);
		}

		public void DeleteVerifiedEmailAddress(String emailAddess)
		{
			var client = SesClient;
			var request = CreateRequest("DeleteVerifiedEmailAddress", WebMethod.Post);
			request.AddParameter("email", emailAddess);

			var response = client.Request(request);
			checkForError(response);
		}

		

		#endregion


		#region API helper methods

		private void checkForError(RestResponseBase response)
		{
			if (response.InnerException != null)
			{
				throw new ApiException("MailChimp Amazon SES Wrapper Exception", response.InnerException);
			}
		}

		protected RestRequest CreateRequest(String path, WebMethod method = WebMethod.Get)
		{
			var request = new RestRequest
			{
			    Method = method,
				Path = path
			};
			request.AddParameter(ParamApiKey, Key);
			return request;
		}

		#endregion
	}
}
